import androidx.compose.desktop.Window
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Notifier
import androidx.compose.ui.window.application
import com.d10ng.stringlib.getAllField
import com.d10ng.stringlib.type.isBoolean
import com.fazecast.jSerialComm.SerialPort
import com.hailiao.bdproto2p1.proto.Proto2p1Basic
import com.hailiao.hlbdcore.annotation.HLBDFieldIn2p1
import com.hailiao.hlbdcore.base.BaseBDProto
import com.hailiao.hlbdcore.base.BaseEnumType
import com.hailiao.hlbdcore.base.BaseInBean
import com.hailiao.hlbdcore.base.BaseOutBean
import kotlinx.coroutines.*
import serialPort.BaudRate
import serialPort.PCSerialPortManager
import tcp.OnNetThreadListener
import tcp.TcpClientManager
import tcp.TcpClientThread
import java.lang.reflect.Field


@OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
    Window(title = "串口桌面程序测试") {

        /** 打印信息 */
        var logText by remember { mutableStateOf("日志信息\r\n") }
        /** 选择的协议 */
        var selectProto by remember { mutableStateOf<BaseBDProto?>(null) }

        val tcpListener = object : OnNetThreadListener{
            override fun onConnected(ipAddress: String) {
                CoroutineScope(Dispatchers.Main).launch {
                    logText += "连接成功" + "\r\n"
                }
            }

            override fun onConnectFailed(ipAddress: String) {
                CoroutineScope(Dispatchers.Main).launch {
                    logText += "连接失败" + "\r\n"
                    TcpClientManager.reconnect()
                }
            }

            override fun onDisconnect(ipAddress: String) {
                CoroutineScope(Dispatchers.Main).launch {
                    logText += "断开连接" + "\r\n"
                    TcpClientManager.reconnect()
                }
            }

            override fun onError(ipAddress: String, error: String) {
                CoroutineScope(Dispatchers.Main).launch {
                    logText += "错误 $error\r\n"
                    TcpClientManager.reconnect()
                }
            }

            override fun onAcceptSocket(ipAddress: String) {
                //
            }

            override fun onReceive(ipAddress: String, port: Int, time: Long, data: ByteArray) {
                CoroutineScope(Dispatchers.Main).launch {
                    logText += "接收 ${data}\r\n"
                }
                CoroutineScope(Dispatchers.IO).launch {
                    PCSerialPortManager.instance()
                        .send(PCSerialPortManager.instance().getUsePortName(), data)
                }
            }
        }

        /** 注册一个消息监听器 */
        MyApp().init({
            println(it.toReadString())
            logText += it.toReadString() + "\r\n"
        }, { str, raw ->
            println(str)
            logText += str + "\r\n"
            TcpClientManager.send(raw)
        })

        MaterialTheme {
            Column {
                TcpClientLayout(tcpListener)
                SerialPortInfoLayout()
                Row {
                    FunctionListLayout(Proto2p1Basic.values().toList()) {
                        selectProto = it
                    }
                    FuncParamsLayout(selectProto) {
                        CoroutineScope(Dispatchers.IO).launch {
                            val outBean: BaseOutBean = PCSerialPortManager.instance()
                                .send(PCSerialPortManager.instance().getUsePortName(), selectProto!!, it)
                            println(outBean.toReadString())
                        }
                    }
                    LogTextLayout(logText)
                }
            }
        }
    }
}

/**
 * TCP转发
 */
@Composable
fun TcpClientLayout(listener: OnNetThreadListener) {
    /** TCP IP地址 */
    var tcpIpAddress by remember { mutableStateOf( "10.0.0.100" ) }
    /** TCP 端口号 */
    var tcpPort by remember { mutableStateOf( "6001" ) }
    Row (
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(5.dp),
        modifier = Modifier.fillMaxWidth()
            .wrapContentHeight()
            .background(Color(0xFFCDCDCD))
            .padding(8.dp)
    ) {
        TextField(
            value = tcpIpAddress,
            onValueChange = {
                tcpIpAddress = it
            },
            label = {
                Text("TCP地址")
            },
            modifier = Modifier
                .wrapContentWidth()
        )
        TextField(
            value = tcpPort,
            onValueChange = {
                tcpPort = it
            },
            label = {
                Text("TCP端口")
            },
            modifier = Modifier
                .wrapContentWidth()
        )
        Button(
            onClick = {
                // 打开TCP
                TcpClientManager.connect(tcpIpAddress, tcpPort.toIntOrNull()?: 6001, listener)
            }
        ) {
            Text("连接")
        }
    }
}

/**
 * 端口信息显示区域
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SerialPortInfoLayout() {
    /** 端口号列表 */
    var comPortList by remember { mutableStateOf( listOf<SerialPort>() ) }
    /** 正在使用的端口 */
    var usePort by remember { mutableStateOf<SerialPort?>(null) }
    /** 是否打开端口 */
    var isOpenPort by remember { mutableStateOf(false) }
    /** 是否显示端口列表 */
    var isShowPortListMenu by remember { mutableStateOf(false) }
    /** 采用的比特率 */
    var baudRate by remember { mutableStateOf(BaudRate.B_115200) }
    /** 是否显示比特率列表 */
    var isShowBaudRateListMenu by remember { mutableStateOf(false) }

    Row (
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(5.dp),
        modifier = Modifier.fillMaxWidth()
            .height(50.dp)
            .background(Color(0xFFEDEDED))
            .padding(8.dp)
    ) {
        // 刷新按钮
        Button(
            onClick = {
                // 刷新列表
                comPortList = PCSerialPortManager.instance().getSerialPortList()
                if (usePort == null && comPortList.isNotEmpty()) {
                    usePort = comPortList[0]
                }
            }
        ) {
            Image(
                bitmap = imageResource("images/img_refresh.png"),
                contentDescription = null,
                modifier = Modifier.fillMaxHeight().wrapContentWidth()
            )
        }
        // 端口号
        Column {
            Box (
                contentAlignment = Alignment.Center,
                modifier = Modifier.wrapContentWidth()
                    .fillMaxHeight()
                    .background(Color(if (isOpenPort) 0xFFAAAAAA else 0xFFCCCCCC), RoundedCornerShape(3.dp))
                    .padding(10.dp, 5.dp)
                    .combinedClickable(
                        enabled = !isOpenPort,
                        onClick = {
                            // 点击切换端口号
                            isShowPortListMenu = true
                        }
                    )
            ) {
                Text(
                    text = usePort?.systemPortName?: "",
                    maxLines = 1,
                    style = MaterialTheme.typography.body1,
                    modifier = Modifier.wrapContentHeight()
                        .defaultMinSize(100.dp)
                )
            }
            // 显示端口列表菜单
            DropdownMenu(
                expanded = isShowPortListMenu,
                onDismissRequest = { isShowPortListMenu = false }
            ) {
                comPortList.forEach { item ->
                    DropdownMenuItem (
                        onClick = {
                            // 点击端口
                            usePort = item
                            isShowPortListMenu = false
                        }
                    ) {
                        Text(text = item.systemPortName)
                    }
                }
            }
        }
        // 波特率
        Column {
            Box (
                contentAlignment = Alignment.Center,
                modifier = Modifier.wrapContentWidth()
                    .fillMaxHeight()
                    .background(Color(if (isOpenPort) 0xFFAAAAAA else 0xFFCCCCCC), RoundedCornerShape(3.dp))
                    .padding(10.dp, 5.dp)
                    .combinedClickable(
                        enabled = !isOpenPort,
                        onClick = {
                            // 点击切换比特率
                            isShowBaudRateListMenu = true
                        }
                    )
            ) {
                Text(
                    text = baudRate.intValue.toString(),
                    maxLines = 1,
                    style = MaterialTheme.typography.body1,
                    modifier = Modifier.wrapContentHeight()
                        .defaultMinSize(100.dp)
                )
            }
            // 显示波特率列表菜单
            DropdownMenu(
                expanded = isShowBaudRateListMenu,
                onDismissRequest = { isShowBaudRateListMenu = false }
            ) {
                BaudRate.values().forEach { item ->
                    DropdownMenuItem (
                        onClick = {
                            // 点击端口
                            baudRate = item
                            isShowBaudRateListMenu = false
                        }
                    ) {
                        Text(text = item.intValue.toString())
                    }
                }
            }
        }
        // 打开端口按钮/关闭端口按钮
        Button(
            enabled = usePort != null,
            onClick = {
                if (isOpenPort) {
                    // 关闭端口
                    PCSerialPortManager.instance().closePort()
                    Notifier().notify("提示", "已关闭端口！")
                    isOpenPort = false
                } else {
                    // 打开端口
                    val isSuccess = PCSerialPortManager.instance().openPort(usePort!!, baudRate)
                    if (isSuccess) {
                        Notifier().notify("提示", "打开端口成功！")
                        isOpenPort = true
                    } else {
                        Notifier().error("错误", "打开端口失败！")
                    }
                }
            }
        ) {
            Text(if (isOpenPort) "关闭端口" else "打开端口")
        }
    }
}

/**
 * 功能列表区域
 */
@Composable
fun FunctionListLayout(protos: List<BaseBDProto>, onSelect: (proto: BaseBDProto) -> Unit) {
    Box (
        modifier = Modifier
            .fillMaxHeight()
            .width(300.dp)
            .background(Color(0xFFFFFFFF))
            .padding(10.dp)
    ) {
        val stateVertical = rememberScrollState(0)
        Box(
            modifier = Modifier
                .verticalScroll(stateVertical)
                .padding(end = 12.dp)
        ) {
            Column (
                verticalArrangement = Arrangement.spacedBy(5.dp)
            ){
                Text(protos[0].type)
                protos.filter { it.inCmd != null }.forEach { proto ->
                    Button(
                        onClick = {
                            onSelect.invoke(proto)
                        }
                    ) {
                        Column {
                            Text("[${proto.inCmd}]")
                            Text(proto.description, fontSize = 12.sp)
                        }
                    }
                }
            }
        }
        // 右侧滚动条
        VerticalScrollbar(
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
            adapter = rememberScrollbarAdapter(stateVertical)
        )
    }
}

/**
 * 功能参数列表
 * @param proto BaseBDProto?
 */
@Composable
fun FuncParamsLayout(proto: BaseBDProto?, onSend: (bean: BaseInBean) -> Unit) {
    proto?: return

    /** 记忆上一次使用的实例 */
    var oldBean by remember { mutableStateOf<BaseInBean?>(null) }

    // 实例
    val clz = proto.inBeanClz.getDeclaredConstructor()
    clz.isAccessible = true
    val newObj = clz.newInstance()
    if (oldBean?.javaClass != proto.inBeanClz) oldBean = newObj
    val obj = oldBean!!
    val fs = obj.getAllField()
    val fList = mutableListOf<Field>()
    for (f in fs) {
        f.getAnnotation(HLBDFieldIn2p1::class.java) ?: continue
        val modifier = java.lang.reflect.Modifier.toString(f.modifiers)
        if (modifier.contains("static") || modifier.contains("final")) continue
        // 将private变量改成外部类可读
        f.isAccessible = true
        fList.add(f)
    }

    // 显示UI
    Box (
        modifier = Modifier
            .fillMaxHeight()
            .width(300.dp)
            .background(Color(0xFFDDDDDD))
            .padding(10.dp)
    ) {
        val stateVertical = rememberScrollState(0)
        Box(
            modifier = Modifier
                .verticalScroll(stateVertical)
                .padding(end = 12.dp)
        ) {
            Column (
                verticalArrangement = Arrangement.spacedBy(5.dp)
            ){
                // 标题
                Text(proto.description)
                // 参数列表
                fList.forEach { f ->
                    FuncParamItemLayout(obj, f) {
                        try {
                            f.set(obj, obj.getValueFromParam2p1(f.type, it))
                        } catch (e: Exception) {
                            e.printStackTrace()
                            f.set(obj, obj.getValueNotNullDefault(f.type))
                        }
                    }
                }
                // 发送按钮
                Button(
                    onClick = {
                        onSend.invoke(obj)
                    }
                ) {
                    Text("发送")
                }
            }
        }
        // 右侧滚动条
        VerticalScrollbar(
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
            adapter = rememberScrollbarAdapter(stateVertical)
        )
    }
}

/**
 * 单项参数
 * @param bean BaseInBean
 * @param f Field
 * @param onChange Function1<[@kotlin.ParameterName] String, Unit>
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FuncParamItemLayout(bean: BaseInBean, f: Field, onChange: (str: String) -> Unit) {
    val ann = f.getAnnotation(HLBDFieldIn2p1::class.java)!!
    val isEdit = !f.type.isBoolean() && f.get(bean) !is BaseEnumType

    /** 变量类型 */
    var oldFieldType by remember { mutableStateOf(f.type) }
    /** 显示值 */
    var fValue by remember { mutableStateOf("") }
    /** 是否显示可选值列表 */
    var isShowMenu by remember { mutableStateOf(false) }

    // 重置变量值
    if (fValue.isNotEmpty() && oldFieldType != f.type) {
        fValue = ""
        oldFieldType = f.type
    }

    Column {
        if (isEdit) {
            TextField(
                value = fValue,
                onValueChange = {
                    fValue = it
                    onChange.invoke(it)
                },
                label = {
                    Text(ann.name)
                },
                modifier = Modifier
                    .fillMaxWidth()
            )
        } else {
            Column {
                Text(ann.name)
                Box (
                    contentAlignment = Alignment.Center,
                    modifier = Modifier.fillMaxWidth()
                        .height(40.dp)
                        .background(Color(0xFFCCCCCC), RoundedCornerShape(3.dp))
                        .padding(10.dp, 5.dp)
                        .combinedClickable(
                            onClick = {
                                isShowMenu = true
                            }
                        )
                ) {
                    Text(
                        text = fValue,
                        maxLines = 1,
                        style = MaterialTheme.typography.body1,
                        modifier = Modifier.fillMaxSize()
                    )
                }
                // 显示菜单
                DropdownMenu(
                    expanded = isShowMenu,
                    onDismissRequest = { isShowMenu = false }
                ) {
                    if (f.type.isBoolean()) {
                        val boolMap = mutableMapOf<String, String>().apply {
                            plus(Pair("true", "1"))
                            plus(Pair("false", "0"))
                        }
                        boolMap.keys.forEach { item ->
                            DropdownMenuItem (
                                onClick = {
                                    // 点击端口
                                    fValue = item
                                    isShowMenu = false
                                    onChange.invoke(boolMap[item]!!)
                                }
                            ) {
                                Text(text = item)
                            }
                        }
                    } else {
                        f.type.enumConstants.forEach { item ->
                            item as BaseEnumType
                            DropdownMenuItem (
                                onClick = {
                                    // 点击端口
                                    fValue = item.tips
                                    isShowMenu = false
                                    onChange.invoke(item.strValue)
                                }
                            ) {
                                Text(text = item.tips)
                            }
                        }
                    }
                }
            }
        }
        Text(ann.description)
    }
}

/**
 * 日志打印信息
 * @param log String
 */
@Composable
fun LogTextLayout(log: String) {
    Box (
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFFCCCCCC))
            .padding(10.dp)
    ) {
        val stateVertical = rememberScrollState(0)
        Box(
            modifier = Modifier
                .verticalScroll(stateVertical, reverseScrolling = true)
                .padding(end = 12.dp)
                .align(Alignment.BottomStart)
        ) {
            BasicTextField(value = log, onValueChange = { })
        }
        // 右侧滚动条
        VerticalScrollbar(
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .fillMaxHeight(),
            adapter = rememberScrollbarAdapter(stateVertical),
            reverseLayout = true
        )
    }
}
